package com.distributed.kv.service;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.distributed.kv.dao.mapper.IdSegmentRepository;
import com.distributed.kv.entity.IdSegment;

/**
 * 分段ID生成服务
 *
 * 实现特点:
 * 1. 分段预分配: 每个实例预申请一段ID,减少分布式同步
 * 2. 双Buffer机制: 当前段快用完时预加载下一段
 * 3. 线程安全: 使用AtomicLong + ReentrantLock保证并发安全
 * 4. 故障恢复: 支持实例重启后恢复ID分配
 * 5. 性能优化: 本地计数器 + 异步预加载
 */
@Service
public class IdGeneratorService {

    private static final Logger logger = LoggerFactory.getLogger(IdGeneratorService.class);

    private final IdSegmentRepository idSegmentRepository;

    @Value("${id-generator.segment.cache-size:1000}")
    private Integer segmentCacheSize;

    @Value("${id-generator.segment.max-retry:3}")
    private Integer maxRetry;

    @Value("${id-generator.segment.step:1000}")
    private Integer step;

    // 当前实例的段标识
    private String instanceKey;

    // 双Buffer: 当前段和下一段
    private volatile IdSegment currentSegment;
    private volatile IdSegment nextSegment;

    // 本地计数器，避免频繁访问数据库
    private final AtomicLong localCounter = new AtomicLong(0);
    private volatile long localMaxId = 0;

    // 并发控制
    private final ReentrantLock segmentLock = new ReentrantLock();
    private volatile boolean isPreloading = false;

    // 统计信息
    private final AtomicLong totalGenerated = new AtomicLong(0);
    private final AtomicLong segmentSwitchCount = new AtomicLong(0);
    
    // 降级模式标识
    private volatile boolean fallbackMode = false;
    private final AtomicLong fallbackCounter = new AtomicLong(System.currentTimeMillis() * 1000);

    public IdGeneratorService(IdSegmentRepository idSegmentRepository) {
        this.idSegmentRepository = idSegmentRepository;
    }

    @PostConstruct
    public void init() {
        try {
            // 生成实例唯一标识
            String hostName = InetAddress.getLocalHost().getHostName();
            String pid = getProcessId();
            this.instanceKey = hostName + "-" + pid + "-" + System.currentTimeMillis();

            logger.info("ID生成器初始化,实例标识: {}", instanceKey);

            // 尝试初始化第一个段
            initializeSegment();

        } catch (Exception e) {
            logger.warn("数据库连接失败，启用内存降级模式: {}", e.getMessage());
            enableFallbackMode();
        }
    }
    
    private void enableFallbackMode() {
        this.fallbackMode = true;
        // 使用时间戳作为起始ID，避免重复
        long startId = System.currentTimeMillis() * 1000;
        this.fallbackCounter.set(startId);
        this.localCounter.set(startId);
        this.localMaxId = startId + 1000000; // 预分配100万个ID
        
        logger.info("内存降级模式已启用，起始ID: {}", startId);
    }

    @PreDestroy
    public void destroy() {
        logger.info("ID生成器销毁,总共生成ID: {}, 段切换次数: {}", 
                   totalGenerated.get(), segmentSwitchCount.get());
    }

    /**
     * 生成下一个ID
     */
    public Long nextId() {
        // 降级模式：直接使用内存计数器
        if (fallbackMode) {
            totalGenerated.incrementAndGet();
            return fallbackCounter.incrementAndGet();
        }
        
        // 先尝试从本地计数器获取
        long currentId = localCounter.incrementAndGet();
        
        if (currentId <= localMaxId) {
            totalGenerated.incrementAndGet();
            return currentId;
        }

        // 本地计数器耗尽，需要切换段
        return switchSegmentAndGetId();
    }

    /**
     * 批量生成ID
     */
    public Long[] nextIds(int count) {
        if (count <= 0) {
            return new Long[0];
        }

        Long[] ids = new Long[count];
        for (int i = 0; i < count; i++) {
            ids[i] = nextId();
        }
        return ids;
    }

    /**
     * 获取生成统计信息
     */
    public IdGeneratorStats getStats() {
        return new IdGeneratorStats(
            instanceKey,
            totalGenerated.get(),
            segmentSwitchCount.get(),
            currentSegment != null ? currentSegment.getCurrentId() : 0L,
            localMaxId,
            localCounter.get()
        );
    }

    /**
     * 获取当前ID值（用于测试和监控）
     */
    public long getCurrentId() {
        return localCounter.get();
    }

    private void initializeSegment() {
        segmentLock.lock();
        try {
            // 尝试从数据库恢复现有段
            IdSegment segment = idSegmentRepository.findBySegmentKey(instanceKey)
                    .orElse(null);

            if (segment == null || segment.isExhausted()) {
                // 申请新段
                segment = allocateNewSegmentFromGlobal();
            }

            this.currentSegment = segment;
            updateLocalCounter(segment);

            // 异步预加载下一段
            preloadNextSegmentAsync();

            logger.info("初始化ID段成功: current={}, max={}, local={}",
                    segment.getCurrentId(), segment.getMaxId(), localMaxId);

        } finally {
            segmentLock.unlock();
        }
    }

    private Long switchSegmentAndGetId() {
        // 降级模式：直接使用内存计数器
        if (fallbackMode) {
            totalGenerated.incrementAndGet();
            return fallbackCounter.incrementAndGet();
        }
        
        segmentLock.lock();
        try {
            // 双重检查，避免重复切换
            long currentId = localCounter.get();
            if (currentId <= localMaxId) {
                totalGenerated.incrementAndGet();
                return currentId;
            }

            // 切换到下一段
            if (nextSegment != null) {
                currentSegment = nextSegment;
                nextSegment = null;
                updateLocalCounter(currentSegment);
                segmentSwitchCount.incrementAndGet();

                // 异步预加载新的下一段
                preloadNextSegmentAsync();

                logger.info("切换到预加载段: current={}, max={}", 
                           currentSegment.getCurrentId(), currentSegment.getMaxId());
            } else {
                // 同步申请新段
                currentSegment = allocateNewSegmentFromGlobal();
                updateLocalCounter(currentSegment);
                segmentSwitchCount.incrementAndGet();

                logger.warn("同步申请新段: current={}, max={}", 
                           currentSegment.getCurrentId(), currentSegment.getMaxId());
            }

            // 返回新段的第一个ID
            long newId = localCounter.incrementAndGet();
            totalGenerated.incrementAndGet();
            return newId;

        } finally {
            segmentLock.unlock();
        }
    }

    private void updateLocalCounter(IdSegment segment) {
        if (segment != null && segment.getCurrentId() != null && segment.getMaxId() != null) {
            localCounter.set(segment.getCurrentId());
            localMaxId = segment.getMaxId();
        }
    }

    private IdSegment allocateNewSegmentFromGlobal() {
        for (int retry = 0; retry < maxRetry; retry++) {
            try {
                // 获取全局计数器段
                IdSegment globalSegment = idSegmentRepository.findBySegmentKey("global-counter")
                        .orElse(new IdSegment("global-counter", step));

                // 使用乐观锁更新全局计数器
                Long currentVersion = globalSegment.getVersion();
                Long newMaxId = (globalSegment.getMaxId() != null ? globalSegment.getMaxId() : 0L) + step;
                Long newCurrentId = newMaxId - step;

                boolean updated = idSegmentRepository.updateSegmentWithVersion(
                        "global-counter",
                        newMaxId,
                        newMaxId,
                        Instant.now(),
                        currentVersion + 1,
                        currentVersion
                );

                if (updated) {
                    // 创建新的实例段
                    IdSegment instanceSegment = new IdSegment(instanceKey, step);
                    instanceSegment.setCurrentId(newCurrentId);
                    instanceSegment.setMaxId(newMaxId);
                    instanceSegment.setUpdatedAt(Instant.now());

                    // 保存实例段
                    idSegmentRepository.save(instanceSegment);

                    logger.info("申请新ID段成功: {}-{}", newCurrentId, newMaxId);
                    return instanceSegment;
                } else {
                    logger.warn("全局计数器更新失败,重试 {}/{}", retry + 1, maxRetry);
                }

            } catch (Exception e) {
                logger.warn("申请ID段失败,重试 {}/{}: {}", retry + 1, maxRetry, e.getMessage());

                if (retry == maxRetry - 1) {
                    throw new RuntimeException("Failed to allocate new ID segment after " + maxRetry + " retries", e);
                }
            }

            // 指数退避
            try {
                Thread.sleep(100 * (1L << retry));
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Interrupted while retrying", ie);
            }
        }

        throw new RuntimeException("Failed to allocate new ID segment");
    }

    @Async
    public CompletableFuture<Void> preloadNextSegmentAsync() {
        if (isPreloading) {
            return CompletableFuture.completedFuture(null);
        }

        return CompletableFuture.runAsync(() -> {
            isPreloading = true;
            try {
                IdSegment segment = allocateNewSegmentFromGlobal();
                nextSegment = segment;
                logger.info("预加载下一段成功: {}-{}", segment.getCurrentId(), segment.getMaxId());
            } catch (Exception e) {
                logger.warn("预加载下一段失败: {}", e.getMessage());
            } finally {
                isPreloading = false;
            }
        });
    }

    /**
     * Java 8 compatible way to get process ID
     */
    private String getProcessId() {
        try {
            String jvmName = ManagementFactory.getRuntimeMXBean().getName();
            return jvmName.split("@")[0];
        } catch (Exception e) {
            logger.warn("Failed to get process ID, using random number", e);
            return String.valueOf(System.currentTimeMillis() % 100000);
        }
    }

    /**
     * ID生成器统计信息
     */
    public static class IdGeneratorStats {
        private final String instanceKey;
        private final long totalGenerated;
        private final long segmentSwitchCount;
        private final long currentSegmentId;
        private final long localMaxId;
        private final long localCurrentId;

        public IdGeneratorStats(String instanceKey, long totalGenerated, long segmentSwitchCount,
                               long currentSegmentId, long localMaxId, long localCurrentId) {
            this.instanceKey = instanceKey;
            this.totalGenerated = totalGenerated;
            this.segmentSwitchCount = segmentSwitchCount;
            this.currentSegmentId = currentSegmentId;
            this.localMaxId = localMaxId;
            this.localCurrentId = localCurrentId;
        }

        // Getters
        public String getInstanceKey() { return instanceKey; }
        public long getTotalGenerated() { return totalGenerated; }
        public long getSegmentSwitchCount() { return segmentSwitchCount; }
        public long getCurrentSegmentId() { return currentSegmentId; }
        public long getLocalMaxId() { return localMaxId; }
        public long getLocalCurrentId() { return localCurrentId; }
        public long getRemainingIds() { return localMaxId - localCurrentId; }

        @Override
        public String toString() {
            return "IdGeneratorStats{" +
                    "instanceKey='" + instanceKey + '\'' +
                    ", totalGenerated=" + totalGenerated +
                    ", segmentSwitchCount=" + segmentSwitchCount +
                    ", currentSegmentId=" + currentSegmentId +
                    ", localMaxId=" + localMaxId +
                    ", localCurrentId=" + localCurrentId +
                    ", remainingIds=" + getRemainingIds() +
                    '}';
        }
    }
}